package org.teamh.gui.engine;

import static org.lwjgl.opengl.GL11.*;

import java.nio.IntBuffer;

import org.lwjgl.BufferUtils;
import org.lwjgl.input.Mouse;
import org.teamh.gui.engine.fbo.Draw2D;
import org.teamh.gui.engine.fbo.FBORenderer;

import de.lessvoid.nifty.Nifty;
import de.lessvoid.nifty.NiftyInputConsumer;
import de.lessvoid.nifty.input.keyboard.KeyboardInputEvent;
import de.lessvoid.nifty.nulldevice.NullSoundDevice;
import de.lessvoid.nifty.renderer.lwjgl.input.LwjglKeyboardInputEventCreator;
import de.lessvoid.nifty.renderer.lwjgl.render.LwjglRenderDevice;
import de.lessvoid.nifty.spi.input.InputSystem;
import de.lessvoid.nifty.spi.time.impl.AccurateTimeProvider;
import de.lessvoid.nifty.tools.resourceloader.NiftyResourceLoader;

public class GUIRenderer implements InputSystem {

	private static final int WIDTH = 1024;
	private static final int HEIGHT = 768;
	
	private FBORenderer offscreenGUIRenderer;
	private OpenGLView openGLView;
	
	private boolean doneNifty = false;
	
	private LwjglKeyboardInputEventCreator keyboardEventCreator = new LwjglKeyboardInputEventCreator();
	
	/**
	 * The Nifty element within the openGL view. May be null, if nifty is not used. 
	 */
	private Nifty nifty;
	
	/**
	 * The renderdevide within nifty. This must be stored due to image loading. 
	 */
	private LwjglRenderDevice lwjglRenderDevice;
	
	private IntBuffer viewportBuffer;
	
	public GUIRenderer(OpenGLView openGLView) {
		this.openGLView = openGLView;
		
		viewportBuffer = BufferUtils.createIntBuffer(16);
	}
	
	public void init() {
		lwjglRenderDevice = new LwjglRenderDevice(false);
		nifty = new Nifty(lwjglRenderDevice, new NullSoundDevice(), this, new AccurateTimeProvider());
		
		offscreenGUIRenderer = FBORenderer.getRenderer("gui", WIDTH, HEIGHT, false);
	}
	
	public void update() {
		if(nifty != null) {
			if(nifty.update()) {
				doneNifty = true;
			}
		}
	}
	
	public boolean isBroken() {
		return doneNifty;
	}
	
	public void render() {
		if(nifty == null) {
			return;
		}
		
		viewportBuffer.rewind();
		glGetInteger(GL_VIEWPORT, viewportBuffer);
		glViewport(0, 0, WIDTH, HEIGHT);
		
		offscreenGUIRenderer.preRender();
		
		glMatrixMode(GL_PROJECTION);
		glPushMatrix();
		{
			glLoadIdentity();
			glOrtho(0, 1024, 768, 0, -9999, 9999);
			glMatrixMode(GL_MODELVIEW);
			glPushMatrix();
			{
				glLoadIdentity();
				boolean depthTest = glIsEnabled(GL_DEPTH_TEST);
				glDisable(GL_DEPTH_TEST);
				boolean texture2D = glIsEnabled(GL_TEXTURE_2D);
				glEnable(GL_TEXTURE_2D);
				glEnable(GL_BLEND);
				glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
				boolean cullFace = glIsEnabled(GL_CULL_FACE);
				glDisable(GL_CULL_FACE);
				glEnable(GL_ALPHA_TEST);
		        glAlphaFunc(GL_NOTEQUAL, 0);
				
				nifty.render(false);
				
				if(depthTest) {
					glEnable(GL_DEPTH_TEST);
				}
				if(!texture2D) {
					glDisable(GL_TEXTURE_2D);
				}
				glDisable(GL_BLEND);
				if(cullFace) {
					glEnable(GL_CULL_FACE);
				}
				glDisable(GL_ALPHA_TEST);
			}
			glPopMatrix();
			glMatrixMode(GL_PROJECTION);
		}
		glPopMatrix();
		glMatrixMode(GL_MODELVIEW);

//		if(true) {
//			return;
//		}
		offscreenGUIRenderer.postRender();
		
		glViewport(viewportBuffer.get(0), viewportBuffer.get(1), viewportBuffer.get(2), viewportBuffer.get(3));
		
		glMatrixMode(GL_PROJECTION);
		glPushMatrix();
		{
			glLoadIdentity();
			glOrtho(0, viewportBuffer.get(2), viewportBuffer.get(3), 0, 0, 1);
			glMatrixMode(GL_MODELVIEW);
			glPushMatrix();
			{
				glLoadIdentity();
				glColor4f(1, 1, 1, 1);
				boolean depthTest = glIsEnabled(GL_DEPTH_TEST);
				glDisable(GL_DEPTH_TEST);
				
				glEnable(GL_BLEND);
				glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
				
				Draw2D.drawImage(offscreenGUIRenderer.getTexID(), 0, 0, viewportBuffer.get(2), viewportBuffer.get(3));
				
				glDisable(GL_BLEND);
				
				if(depthTest) {
					glEnable(GL_DEPTH_TEST);
				}
			}
			glPopMatrix();
			glMatrixMode(GL_PROJECTION);
		}
		glPopMatrix();
		glMatrixMode(GL_MODELVIEW);
	}
	
	public void processKeyboardEvent(boolean forceCharacter, int eventKey, char eventChar, boolean state, NiftyInputConsumer consumer) {
		if(nifty != null) {
			KeyboardInputEvent event = null;
			
			/*
			 * These two lines here are the bugfix for writing the @ in Windows with nifty.  
			 */
			if(forceCharacter) {
				event = keyboardEventCreator.createForcedCharacterEvent(eventKey, eventChar, state);
			} else {
				event = keyboardEventCreator.createEvent(eventKey, eventChar, state);
			}
			
			consumer.processKeyboardEvent(event);
		}
	}
	
	public void processMouseEvent(int x, int y, int wheel, int button, boolean state, NiftyInputConsumer consumer) {
		if(consumer != null) {
			x -= viewportBuffer.get(0);
			x = (int)((float)x * WIDTH / viewportBuffer.get(2));
			y += viewportBuffer.get(1);
			y = openGLView.getHeight() - y;
			y = (int)((float)y * HEIGHT / viewportBuffer.get(3));
			consumer.processMouseEvent(x, y, wheel / 120, button, state);
		}
	}
	
	@Override
	public void setMousePosition(int x, int y) {
		Mouse.setCursorPosition(x, openGLView.getHeight() - y);
	}
	
	@Override
	public void setResourceLoader(NiftyResourceLoader resLoader) {
		//What exactly does this function??
	}
	
	@Override
	public void forwardEvents(NiftyInputConsumer inputConsumer) {
		openGLView.manageInput(inputConsumer);
	}
	
	public Nifty getNifty() {
		return nifty;
	}
	
	public LwjglRenderDevice getLwjglRenderDevice() {
		return lwjglRenderDevice;
	}
}
